#include	"server.h"

using namespace std;

/* 
 * A concurrent tcp server that echos income packets 
 */

/* global variables */
int      debug;
uint16_t serverport;
uint16_t listenqsize;

static void
initialize_parameters(void) 
{
  serverport  = SERVERPORT;
  listenqsize = LISTENQSIZE; 
  debug = 0;
}

static void
usage(char *program)
{
  fprintf(stderr, "usage: %s [-p port] [-q qsize] [-d]\n", program);
  exit(1);
}

static void
parse_args(int argc, char** argv) 
{
  int n = 1;
  while (n < argc) {
    if (strcmp(argv[n], "-d") == 0) {
      debug = 1;
    } else if (strcmp(argv[n], "-p") == 0) {
      serverport = atoi(argv[++n]);
    } else if (strcmp(argv[n], "-q") == 0) {
      listenqsize = atoi(argv[++n]);
    } else
      usage(argv[0]);
    n++;
  }
}

int
main(int argc, char **argv)
{
  int                 listenfd, connectionfd;
  struct sockaddr_in  clientsockaddr, serversockaddr;
  socklen_t	      clientsocklen;

  // initialize setup parameters such as server port number, debug
  initialize_parameters();

  // parse args, to overwrite the parameters
  parse_args(argc, argv);

  // setup signal handler for SIGCHLD to avoid zombie children
  signal(SIGCHLD, sig_child);
  // setup signal handler for other signals
  signal(SIGTERM, sig_term);
  signal(SIGINT,  sig_int);
  signal(SIGHUP,  sig_hup);
  signal(SIGUSR1, sig_usr1);
  signal(SIGPIPE, SIG_IGN);

  // create listening socket
  if ( (listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
    err_sys("socket error");
  }

  // fill in and bind address
  bzero(&serversockaddr, sizeof(serversockaddr));
  serversockaddr.sin_family      = AF_INET;
  serversockaddr.sin_addr.s_addr = htonl(INADDR_ANY);
  serversockaddr.sin_port        = htons(serverport);
  if (bind(listenfd, (SA *) &serversockaddr, sizeof(serversockaddr)) < 0)
    err_sys("bind error");

  // enter listen state
  if (listen(listenfd, listenqsize) < 0)
    err_sys("listen error");

  int           i, n, maxi, maxfd, sockfd;
  char          line[MAXLINE];
  int           nready, client[FD_SETSIZE];
  socketstream *ss[FD_SETSIZE];

  fd_set        rset, allset;


  maxfd = listenfd;			/* initialize */
  maxi = -1;				/* index into client[] array */
  for (i = 0; i < FD_SETSIZE; i++)
    client[i] = -1;			/* -1 indicates available entry */
  FD_ZERO(&allset);
  FD_SET(listenfd, &allset);
  /* end fig01 */
  
  for ( ; ; ) {

    rset = allset;		/* structure assignment */
    nready = select(maxfd+1, &rset, NULL, NULL, NULL);
    
    if (FD_ISSET(listenfd, &rset)) {	/* new client connection */
      clientsocklen = sizeof(clientsockaddr);
      connectionfd = accept(listenfd, (SA *) &clientsockaddr, &clientsocklen);

      if (debug) {
	char clientname[MAXLINE];
	cerr << "New client: ";
	cerr << inet_ntop(AF_INET, &clientsockaddr.sin_addr, clientname, sizeof(clientname));
	cerr << " port " << ntohs(clientsockaddr.sin_port) << endl;
      }
      
      for (i = 0; i < FD_SETSIZE; i++)
	if (client[i] < 0) {
	  client[i] = connectionfd;	/* save descriptor */
	  ss[i] = new socketstream(connectionfd);
	  break;
	}

      if (i == FD_SETSIZE)
	err_quit("too many clients");
      
      FD_SET(connectionfd, &allset);	/* add new descriptor to set */
      if (connectionfd > maxfd)
	maxfd = connectionfd;		/* for select */

      if (i > maxi)
	maxi = i;			/* max index in client[] array */
      
      if (--nready <= 0)
	continue;		        /* no more readable descriptors */
    }  // end of if (FD_ISSET(listenfd, ...)
    
    for (i = 0; i <= maxi; i++) {	/* check all clients for data */
      if ( (sockfd = client[i]) < 0)
	continue;
      if (FD_ISSET(sockfd, &rset)) {
	if ( (n = ss[i]->read_line(line, MAXLINE)) == 0) {
	  /* connection closed by client */
	  close(sockfd);
	  FD_CLR(sockfd, &allset);
	  client[i] = -1;
	  delete ss[i];
	} else
	  ss[i]->writen_socket(line, n);
	
	if (--nready <= 0)
	  break;		        /* no more readable descriptors */
      }
    } /* end of for (i=0; ...) */
  } /* end of for (; ; ) */
} 

